package apiserver

import (
	"fmt"
	"net/http"
	"reflect"
	"runtime"
	"strings"
	"sync"

	"yingsheng.com/go-api-server/config"
	"yingsheng.com/go-api-server/constant"
	"yingsheng.com/go-api-server/context"
	"yingsheng.com/go-api-server/errors"
	"yingsheng.com/go-api-server/log"
	"yingsheng.com/go-api-server/utils"
)

type FilterFunc func(*context.Context)

type controllerInfo struct {
	controllerType reflect.Type
}

// 服务注册结构体
type ControllerRegister struct {
	routers map[string]*controllerInfo
	pool    sync.Pool
}

// 创建新的控制器注册服务
func NewControllerRegister() *ControllerRegister {
	cr := &ControllerRegister{
		routers: make(map[string]*controllerInfo),
	}

	cr.pool.New = func() interface{} {
		return context.NewContext(ApiApp.Module, config.Configer.IsDev())
	}

	return cr
}

// 注册控制器
func (r *ControllerRegister) Add(actionController string, c ControllerInterface) {
	actionController = config.Configer.ApiConf.ModuleName + "." + strings.ToLower(actionController)
	if _, ok := r.routers[actionController]; ok {
		panic(actionController + " 已经存在，控制器注册失败!")
	}

	reflectVal := reflect.ValueOf(c)
	t := reflect.Indirect(reflectVal).Type()
	r.routers[actionController] = &controllerInfo{controllerType: t}
}

// 接管请求事件
func (r *ControllerRegister) ServeHTTP(httpRes http.ResponseWriter, httpReq *http.Request) {
	ApiApp.waitGroup.Add(1)

	ctx := r.pool.Get().(*context.Context)
	ctx.Reset(httpReq, httpRes)

	// 兼容chrome浏览器，避免多次请求
	if strings.EqualFold(httpReq.URL.String(), "/favicon.ico") {
		log.Logger.Info("过滤chrome浏览器favicon.ico请求")
		ctx.Response.WriteError(http.StatusNotFound)
		return
	}

	res := ctx.Response
	req := ctx.Request
	action := req.Action()

	defer r.pool.Put(ctx)
	defer r.recoverPanic(ctx)

	//执行过滤器
	for _, ff := range ApiApp.filterFunc {
		ff(ctx)
	}

	if len(action) == 0 {
		res.Error("接口名不能为空")
	} else if action == "heartbeat" {
		ctx.Response.WriteError(http.StatusOK)
		return
	} else {
		ctrl := ""
		actions := strings.Split(strings.ToLower(action), ".")
		count := len(actions)
		if count > 2 {
			for i := 0; i < count-1; i++ {
				if len(ctrl) > 0 {
					ctrl += "."
				}

				ctrl += actions[i]
			}
		}

		if runObject, ok := r.routers[ctrl]; ok {
			//Invoke the request handler
			vc := reflect.New(runObject.controllerType)

			execController, ok := vc.Interface().(ControllerInterface)
			if !ok {
				panic("该控制器没有实现标准控制器接口")
			}

			execController.Init(ctx)

			execController.Prepare()

			execController.FuncMapping()

			// 执行服务方法
			if err := execController.HandlerFunc(actions[count-1]); err == nil {
				execController.Finish()
			} else {
				res.Error(err.Error())
			}

		} else {
			res.Error("不存在" + ctrl + "接口")
		}
	}

	r.done(ctx)
}

// 请求事件异常捕获
func (r *ControllerRegister) recoverPanic(ctx *context.Context) {
	if e := recover(); e != nil {
		if e == errors.StopError {
			//ctrl stop
			r.done(ctx)
			return
		}

		code := utils.NewRandomNum(6)
		errorInfo := fmt.Sprintf("系统故障[%v]: %v", code, e)
		errorInfo += "\n故障堆栈："
		for i := 1; ; i += 1 {
			_, file, line, ok := runtime.Caller(i)
			if !ok {
				break
			} else {
				errorInfo += "\n"
			}
			errorInfo += fmt.Sprintf("%v %v", file, line)
		}

		// 记录到链式日志错误信息
		ctx.ChainError(errorInfo)

		ctx.Response.Error(constant.RESPONSE_CODE_CRASH, "系统故障："+code)

		r.done(ctx)
	}
}

func (r *ControllerRegister) done(ctx *context.Context) {
	ctx.Response.WriteJson()

	//这里没有记录消息发送失败日志
	err := ApiApp.ChainProducer.Publish(ctx.ChainToJson())
	if err != nil {
		log.Logger.Error("chain日志发送消息失败：", err)
	}

	// 完成请求事件
	ApiApp.waitGroup.Done()
}
